using System;
using System.CodeDom;
using System.Collections.Generic;
using InterfaceWeaver.AopCore;
using Roslyn.Compilers.CSharp;

namespace InterfaceWeaver.CustomTool
{
    public class ImplementICloneableAspectHandler : AspectHandlerBase<ImplementICloneableAttribute>
    {
        /// <summary>
        /// Handles the on member.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <param name="propertyDeclarationSyntax"> </param>
        /// <param name="handlerStreams"> </param>
        /// <returns>
        /// True if the aspect is handled, false otherwise
        /// </returns>
        public override void HandleOnProperty(AspectDispatcher context, AttributeSyntax attributeSyntax, PropertyDeclarationSyntax propertyDeclarationSyntax, IDictionary<PropertyDeclarationSyntax, AspectTuple[]> handlerStreams)
        {
            var currentProperty = context.WeavedCodeContext.CurrentCodeMemberPropertyDeclaration.CurrentCodeMemberPropertyDeclaration;
            var currentType = context.WeavedCodeContext.CurrentTypeDeclaration;

            // Ensure ICloneable implementation
            context.WeavedCodeContext.CurrentTypeDeclaration.ImplementICloneable();
            var codeCloneMethod = currentType.FindICloneableCloneMethod();

            // Prepare clone source and target declarations
            var sourcePropertyReference = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), currentProperty.Name);
            var cloneAssignementTargetReference = new CodePropertyReferenceExpression(
                            new CodeVariableReferenceExpression("clone"),
                            context.WeavedCodeContext.CurrentCodeMemberPropertyDeclaration.CurrentCodeMemberPropertyDeclaration.Name);


            // Clone mode implementation cases
            CodeStatement[] cloneSequence = null;
            switch (this.DetermineImplementationMode(attributeSyntax))
            {
                case ICloneableImplementationMode.ShallowClone:
                    cloneSequence = new CodeStatement[] { new CodeAssignStatement(cloneAssignementTargetReference, sourcePropertyReference) };
                    break;

                case ICloneableImplementationMode.DeepCloneSafe:

                    cloneSequence = sourcePropertyReference.CodeSafeCloneAssignementSequence(
                        cloneAssignementTargetReference,
                        currentProperty.Type,
                        String.Format("local{0}TempBoxedBackup", sourcePropertyReference.PropertyName));
                    
                    break;

                case ICloneableImplementationMode.DeepCloneForced:
                    cloneSequence = sourcePropertyReference.CodeForcedCloneAssignementSequence(
                        cloneAssignementTargetReference,
                        currentProperty.Type);
                    break;
            }

            // Add the choosen implementation to method statment
            if (cloneSequence != null)
            {
                foreach (var codeStatement in cloneSequence)
                {
                    codeCloneMethod.Statements.Insert(codeCloneMethod.Statements.Count - 1, codeStatement);
                }
            }
        }

        /// <summary>
        /// Determines the implementation mode.
        /// </summary>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <returns></returns>
        private ICloneableImplementationMode DetermineImplementationMode(AttributeSyntax attributeSyntax)
        {
            var mode = ICloneableImplementationMode.ShallowClone;

            // Determining the clone mode
            if (attributeSyntax.HasParameter("Mode"))
            {
                var modeValueText = attributeSyntax.GetParameterValueText("Mode");
                var enumTypeText = typeof(ICloneableImplementationMode).Name;
                if (modeValueText == string.Format("{0}.{1}", enumTypeText, ICloneableImplementationMode.DeepCloneSafe))
                {
                    mode = ICloneableImplementationMode.DeepCloneSafe;
                }
                else if (modeValueText == string.Format("{0}.{1}", enumTypeText, ICloneableImplementationMode.DeepCloneForced))
                {
                    mode = ICloneableImplementationMode.DeepCloneForced;
                }
            }

            return mode;
        }
    }
}